﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace Noris.Schedule.Support.Services
{
	// DEKLARACE interface a args PRO SLUŽBY TYPU "GLOBÁLNÍ FUNKCE V TOOLBARU"
	#region INTERFACE IFunctionGlobal : předpis pro funkci, která má být zobrazena v TOOLBARU
	/// <summary>
	/// IFunctionGlobal : předpis pro funkci, která má být zobrazena v TOOLBARU
	/// </summary>
	public interface IFunctionGlobal : IPlugin
	{
		/// <summary>
		/// Metoda, kde globální funkce může deklarovat svoje funkční položky (položka Toolbaru).
		/// Standardní postup je, že se pomocí metody args.AddItem(...) vloží položky globálních funkcí.
		/// </summary>
		/// <param name="args"></param>
		void CreateToolItem(FunctionGlobalCreateArgs args);
		/// <summary>
		/// Metoda, kdy uživatel zvolil jednu konkrétní globální funkci, UI to detekovalo, našlo garanta,
		/// a nyní volá garanta funkce a chce, aby funkci provedl.
		/// Zdejší metoda má dvě možnosti:
		/// a) provést celou funkci sama,
		/// anebo b) do argumentu nastavit do Result* požadavky tak, aby volající kód návazně vyvolal request do datových zdrojů.
		/// Více viz argument, property ResultCallDataSourceRequest a ResultCallRequestData.
		/// </summary>
		/// <param name="args"></param>
		void RunToolItem(FunctionGlobalRunArgs args);
	}
	#endregion
	#region TŘÍDY ARGUMENTŮ FunctionGlobalCreateArgs, FunctionGlobalRunArgs
	/// <summary>
	/// Argument s daty pro podporu tvorby deklarace funkce (v metodě IFunctionGlobal.CreateToolItem())
	/// </summary>
	public class FunctionGlobalCreateArgs
	{
		/// <summary>
		/// Konstruktor argumentu pro Create
		/// </summary>
		public FunctionGlobalCreateArgs()
		{
			this.Items = new List<FunctionGlobalItem>();
		}
		/// <summary>
		/// Seznam položek. Jeden garant může deklarovat více funkcí najednou.
		/// Jednotlivé položky mají mít naplněnu property FunctionID, aby garant v metodě RunToolItem() poznal, která funkce se volá.
		/// </summary>
		public List<FunctionGlobalItem> Items { get; set; }
		/// <summary>
		/// Přidá do svého seznamu funkcí další položku.
		/// </summary>
		/// <param name="creator"></param>
		/// <param name="item"></param>
		/// <returns></returns>
		public void AddItem(FunctionGlobalItem item)
		{
			if (this.Items == null)
				this.Items = new List<FunctionGlobalItem>();
			this.Items.Add(item);
		}
	}
	/// <summary>
	/// Parametry pro spouštění jedné konkrétní funkce
	/// </summary>
	public class FunctionGlobalRunArgs
	{
		#region KONSTRUKCE
		/// <summary>
		/// Konstruktor argumentu pro Run
		/// </summary>
		/// <param name="runItem"></param>
		public FunctionGlobalRunArgs(FunctionGlobalItem runItem, FindDataSource getDs, FindDataSource tryGetDs)
		{
			this.RunItem = runItem;
			this.ResultCallDataSourceRequest = false;
			this._GetExternalDataSource = getDs;
			this._TryGetExternalDataSource = tryGetDs;
		}
		private FindDataSource _GetExternalDataSource;
		private FindDataSource _TryGetExternalDataSource;
		#endregion
		#region PROPERTY
		/// <summary>
		/// Metoda se pokusí přetypovat FunctionID na požadovaný typ.
		/// Vrací true = podařilo se / false = nepodařilo se.
		/// Hodnotu ukládá do out parametru value.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public bool TryGetFunctionID<T>(out T value)
		{
			value = default(T);
			IComparable functionID = this.FunctionID;
			if (functionID == null || (!(functionID is T))) return false;
			value = (T)functionID;
			return true;
		}
		/// <summary>
		/// FunctionID funkce, která se zpracovává
		/// </summary>
		public IComparable FunctionID { get { return (this.RunItem == null ? null : this.RunItem.FunctionID); } }
		/// <summary>
		/// Tag funkce, která se zpracovává
		/// </summary>
		public object Tag { get { return (this.RunItem == null ? null : this.RunItem.Tag); } }
		/// <summary>
		/// Položka, kterou si uživatel vybral ke spuštění.
		/// UI poznalo, že garantem této položky je volaná třída.
		/// Tato třída si má přečíst RunItem.FunctionID a podle toho má vykonat funkci, pro kterou sama vygenerovala tuto položku.
		/// </summary>
		public FunctionGlobalItem RunItem { get; protected set; }
		/// <summary>
		/// Výsledek běhu funkce: funkce si může vyžádat reload všech dat do grafu.
		/// Default = false.
		/// </summary>
		public bool ResultReloadAllRows { get; set; }
		/// <summary>
		/// Výsledek běhu funkce: funkce se může rozhodnout (jako svůj result),
		/// že se má do datových zdrojů poslat požadavek (request) typu DataSourceRequestType.RunDataFunction.
		/// Default = false (požadavek se nebude posílat).
		/// </summary>
		public bool ResultCallDataSourceRequest { get; set; }
		/// <summary>
		/// Výsledek běhu funkce: pokud se funkce rozhodne, že chce poslat požadavek do datového zdroje
		/// (tj. když nastaví ResultCallDataSourceRequest = true), pak by měla určit i typ datového zdroje, do kterého chce požadavek poslat.
		/// Udělá to tak, že sem vloží typ konkrétního datového zdroje, který má požadavek vyřídit.
		/// Pokud sem nic nevloží, pak Schedule pošle požadavek do všech datových zdrojů, a je na nich zda se k požadavku přhlásí.
		/// To se řeší tak, že datový zdroj pozná / nepozná typ a hodnotu uloženou v this.ResultCallRequestData.
		/// Default = null (požadavek se bude posílat do všech zdrojů).
		/// </summary>
		public Type ResultCallDataSourceOfType { get; set; }
		/// <summary>
		/// Specifikace funkce, kterou má provést datový zdroj při požadavku DataSourceRequestType.RunDataFunction.
		/// Tato hodnota se předá v property DataSourceRequestDataFunction.RunRequestData (určuje konkrétní funkci).
		/// </summary>
		public IComparable ResultCallRequestData { get; set; }
		#endregion
		#region ODKAZY NA METODY PRO HLEDÁNÍ EXTERNÍHO DATOVÉHO ZDROJE
		/// <summary>
		/// Metoda najde a vrátí datový zdroj daného typu.
		/// Pokud jej nenajde, bude vyhozena výjimka.
		/// </summary>
		public FindDataSource GetExternalDataSource { get { return this._GetExternalDataSource; } }
		/// <summary>
		/// Metoda najde a vrátí datový zdroj daného typu.
		/// Pokud jej nenajde, vrátí null, datový zdroj hledaného typu neexistuje.
		/// </summary>
		public FindDataSource TryGetExternalDataSource { get { return this._TryGetExternalDataSource; } }
		#endregion
	}
	#endregion
	#region CLASS FunctionGlobalItem : JEDNA POLOŽKA GLOBÁLNÍ FUNKCE + enums
	/// <summary>
	/// Jedna položka, deklarující globální funkci.
	/// Obsahuje identifikátor garanta funkce, který umožní najít jejího garanta a zavolat jej.
	/// Obsahuje identifikátor samotné funkce, aby garant poznal svoji funkci.
	/// Obsahuje definici uživatelského rozhraní (typ položky {button / dropdown button / combo}, text, tooltip, image).
	/// Umožňuje do sebe vkládat další tytéž položky (dropdown menu, sub-menu, combo list)
	/// </summary>
	public class FunctionGlobalItem
	{
		#region KONSTRUKCE
		/// <summary>
		/// Konstruktor položky globální funkce
		/// </summary>
		/// <param name="creatorType"></param>
		/// <param name="functionID"></param>
		/// <param name="itemType"></param>
		/// <param name="text"></param>
		/// <param name="toolTipText"></param>
		/// <param name="image"></param>
		public FunctionGlobalItem(Type creatorType, IComparable functionID, FunctionGlobalItemType itemType, string text, string toolTipText, Image image)
		{
			this.CreatorType = creatorType;
			this.FunctionID = functionID;
			this.ItemType = itemType;
			this.Text = text;
			this.ToolTipText = toolTipText;
			this.Image = image;
		}
		/// <summary>
		/// Typ tvůrce = garant funkce, ten ji bude obsluhovat v případě její aktivace
		/// </summary>
		public Type CreatorType { get; protected set; }
		/// <summary>
		/// Identifikátor funkce = z hlediska jednoho garanta by mělo být jednoznačné
		/// (garant si podle tohoto ID určí, která položka byla vybrána - jeden garant může deklarovat a obsluhovat víc než jednu funkci)
		/// </summary>
		public IComparable FunctionID { get; protected set; }
        /// <summary>
        /// Pouze pro ComboBox: hodnota ID položky, kterou má Toolbar vybrat po zobrazení (včetně logiky Click => volání funkce). null = žádnou, combo bude po startu nevyplněné.
        /// </summary>
        public IComparable SelectItemID { get; protected set; }
		/// <summary>
		/// Vizuální druh položky
		/// </summary>
		public FunctionGlobalItemType ItemType { get; protected set; }
		/// <summary>
		/// Text funkce. Pokud bude Empty, nebude zobrazen.
		/// Pokud bude vyplněn, pak pro typ Button bude zobrazen za Image.
		/// </summary>
		public string Text { get; set; }
		/// <summary>
		/// Tooltip
		/// </summary>
		public string ToolTipText { get; set; }
		/// <summary>
		/// Obrázek. Měl by pocházet z volatilního zdroje (soubor), ne z cache (z paměti).
		/// Je to proto, že UI občas (při nastavení Disable) do obrázku zasahuje, a pokud obrázek pochází z paměti, pak .NET zkolabuje.
		/// </summary>
		public Image Image { get; set; }
		/// <summary>
		/// Seznam sub položek. Uplatní se jen podle typu této položky (this.ItemType).
		/// </summary>
		public List<FunctionGlobalItem> SubItems
		{
			get
			{
				if (this._SubItems == null)
					this._SubItems = new List<FunctionGlobalItem>();
				return this._SubItems;
			}
			set
			{
				this._SubItems = value;
			}
		}
		private List<FunctionGlobalItem> _SubItems;
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Text;
        }
		/// <summary>
		/// Libovolná další informace, kterou si garant uloží při vytváření položky (v metodě IFunctionGlobal.CreateToolItem()),
		/// a kterou si pak může přečíst (v IFunctionGlobal.RunToolItem()).
		/// </summary>
		public object Tag { get; set; }
		#endregion
		#region STATICKÉ TYPOVÉ KONSTRUKTORY
		/// <summary>
		/// Vrátí položku, která zajistí zobrazení separátoru
		/// </summary>
		/// <returns></returns>
		public static FunctionGlobalItem CreateSeparator()
		{
			return new FunctionGlobalItem(null, null, FunctionGlobalItemType.Separator, String.Empty, null, null);
		}
		/// <summary>
		/// Vrátí položku, která bude zobrazena jako button
		/// </summary>
		/// <param name="creator">Ten, kdo tuto položku vytváří. Předává se this.</param>
		/// <param name="functionID">Identifikátor funkce. Enum je nejvhodnější. V konkrétní aplikaci se přetypuje zpátky. Tato hodnota přichází do cílových metod typicky v property RequestData.</param>
		/// <param name="image">Obrázek, může být null</param>
		/// <param name="toolTipText">Nápověda</param>
		/// <returns></returns>
		public static FunctionGlobalItem CreateButton(object creator, IComparable functionID, Image image, string toolTipText)
		{
			return new FunctionGlobalItem(creator.GetType(), functionID, FunctionGlobalItemType.Button, String.Empty, toolTipText, image);
		}
		/// <summary>
		/// Vrátí položku, která bude zobrazena jako button
		/// </summary>
		/// <param name="creator">Ten, kdo tuto položku vytváří. Předává se this.</param>
		/// <param name="functionID">Identifikátor funkce. Enum je nejvhodnější. V konkrétní aplikaci se přetypuje zpátky. Tato hodnota přichází do cílových metod typicky v property RequestData.</param>
		/// <param name="image">Obrázek, může být null</param>
		/// <param name="text">Text do buttonu</param>
		/// <param name="toolTipText">Nápověda</param>
		/// <returns></returns>
		public static FunctionGlobalItem CreateButton(object creator, IComparable functionID, Image image, string text, string toolTipText)
		{
			return new FunctionGlobalItem(creator.GetType(), functionID, FunctionGlobalItemType.Button, text, toolTipText, image);
		}
		/// <summary>
		/// Vrátí položku, která bude zobrazena jako dropdown button
		/// </summary>
		/// <param name="creator">Ten, kdo tuto položku vytváří. Předává se this.</param>
		/// <param name="functionID">Identifikátor funkce. Enum je nejvhodnější. V konkrétní aplikaci se přetypuje zpátky. Tato hodnota přichází do cílových metod typicky v property RequestData.</param>
		/// <param name="image">Obrázek, může být null</param>
		/// <param name="toolTipText">Nápověda</param>
		/// <param name="subItems">Seznam podřízených položek</param>
		/// <returns></returns>
		public static FunctionGlobalItem CreateDropDownButton(object creator, IComparable functionID, Image image, string toolTipText, IEnumerable<FunctionGlobalItem> subItems)
		{
			FunctionGlobalItem item = new FunctionGlobalItem(creator.GetType(), functionID, FunctionGlobalItemType.DropDownButton, String.Empty, toolTipText, image);
			item.AddSubItems(subItems, FunctionGlobalItemType.Button, FunctionGlobalItemType.DropDownButton);
			return item;
		}
		/// <summary>
		/// Vrátí položku, která bude zobrazena jako DropDownButton
		/// </summary>
		/// <param name="creator">Ten, kdo tuto položku vytváří. Předává se this.</param>
		/// <param name="functionID">Identifikátor funkce. Enum je nejvhodnější. V konkrétní aplikaci se přetypuje zpátky. Tato hodnota přichází do cílových metod typicky v property RequestData.</param>
		/// <param name="image">Obrázek, může být null</param>
		/// <param name="text">Text do buttonu</param>
		/// <param name="toolTipText">Nápověda</param>
		/// <param name="subItems">Seznam podřízených položek, typu Button nebo DropDownButton</param>
		/// <returns></returns>
		public static FunctionGlobalItem CreateDropDownButton(object creator, IComparable functionID, Image image, string text, string toolTipText, IEnumerable<FunctionGlobalItem> subItems)
		{
			FunctionGlobalItem item = new FunctionGlobalItem(creator.GetType(), functionID, FunctionGlobalItemType.DropDownButton, text, toolTipText, image);
			item.AddSubItems(subItems, FunctionGlobalItemType.Button, FunctionGlobalItemType.DropDownButton);
			return item;
		}
		/// <summary>
		/// Vrátí položku, která bude zobrazena jako dropdown button
		/// </summary>
		/// <param name="creator">Ten, kdo tuto položku vytváří. Předává se this.</param>
		/// <param name="functionID">Identifikátor funkce. Enum je nejvhodnější. V konkrétní aplikaci se přetypuje zpátky. Tato hodnota přichází do cílových metod typicky v property RequestData.</param>
		/// <param name="image">Obrázek, může být null</param>
		/// <param name="toolTipText">Nápověda</param>
		/// <param name="subItems">Seznam podřízených položek</param>
		/// <returns></returns>
		public static FunctionGlobalItem CreateDropDownButton(object creator, IComparable functionID, Image image, string toolTipText, params FunctionGlobalItem[] subItems)
		{
			FunctionGlobalItem item = new FunctionGlobalItem(creator.GetType(), functionID, FunctionGlobalItemType.DropDownButton, String.Empty, toolTipText, image);
			item.AddSubItems(subItems, FunctionGlobalItemType.Button, FunctionGlobalItemType.DropDownButton);
			return item;
		}
		/// <summary>
		/// Vrátí položku, která bude zobrazena jako DropDownButton
		/// </summary>
		/// <param name="creator">Ten, kdo tuto položku vytváří. Předává se this.</param>
		/// <param name="functionID">Identifikátor funkce. Enum je nejvhodnější. V konkrétní aplikaci se přetypuje zpátky. Tato hodnota přichází do cílových metod typicky v property RequestData.</param>
		/// <param name="image">Obrázek, může být null</param>
		/// <param name="text">Text do buttonu</param>
		/// <param name="toolTipText">Nápověda</param>
		/// <param name="subItems">Seznam podřízených položek, typu Button nebo DropDownButton</param>
		/// <returns></returns>
		public static FunctionGlobalItem CreateDropDownButton(object creator, IComparable functionID, Image image, string text, string toolTipText, params FunctionGlobalItem[] subItems)
		{
			FunctionGlobalItem item = new FunctionGlobalItem(creator.GetType(), functionID, FunctionGlobalItemType.DropDownButton, text, toolTipText, image);
			item.AddSubItems(subItems, FunctionGlobalItemType.Button, FunctionGlobalItemType.DropDownButton);
			return item;
		}
		/// <summary>
		/// Vrátí položku, která bude zobrazena jako ComboBox
		/// </summary>
		/// <param name="creator">Ten, kdo tuto položku vytváří. Předává se this.</param>
		/// <param name="functionID">Identifikátor funkce. Enum je nejvhodnější. V konkrétní aplikaci se přetypuje zpátky. Tato hodnota přichází do cílových metod typicky v property RequestData.</param>
		/// <param name="toolTipText">Nápověda</param>
		/// <param name="subItems">Seznam podřízených položek, typu ComboItem</param>
		/// <returns></returns>
		public static FunctionGlobalItem CreateComboBox(object creator, IComparable functionID, string toolTipText, IEnumerable<FunctionGlobalItem> subItems)
		{
			FunctionGlobalItem item = new FunctionGlobalItem(creator.GetType(), functionID, FunctionGlobalItemType.ComboBox, String.Empty, toolTipText, null);
			item.AddSubItems(subItems, FunctionGlobalItemType.ComboItem);
            item.SelectFirstItem();
			return item;
		}
        /// <summary>
        /// Vrátí položku, která bude zobrazena jako ComboBox
        /// </summary>
        /// <param name="creator">Ten, kdo tuto položku vytváří. Předává se this.</param>
        /// <param name="toolTipText">Nápověda</param>
        /// <param name="subItems">Seznam podřízených položek, typu ComboItem</param>
        /// <returns></returns>
        public static FunctionGlobalItem CreateComboBox(object creator, string toolTipText, bool selectFirst, IEnumerable<FunctionGlobalItem> subItems)
        {
            FunctionGlobalItem item = new FunctionGlobalItem(creator.GetType(), null, FunctionGlobalItemType.ComboBox, String.Empty, toolTipText, null);
            item.AddSubItems(subItems, FunctionGlobalItemType.ComboItem);
            if (selectFirst) item.SelectFirstItem();
            return item;
        }
        /// <summary>
        /// Vrátí položku, která bude zobrazena jako ComboBox
        /// </summary>
        /// <param name="creator">Ten, kdo tuto položku vytváří. Předává se this.</param>
        /// <param name="toolTipText">Nápověda</param>
        /// <param name="subItems">Seznam podřízených položek, typu ComboItem</param>
        /// <returns></returns>
        public static FunctionGlobalItem CreateComboBox(object creator, string toolTipText, bool selectFirst, params FunctionGlobalItem[] subItems)
        {
            FunctionGlobalItem item = new FunctionGlobalItem(creator.GetType(), null, FunctionGlobalItemType.ComboBox, String.Empty, toolTipText, null);
            item.AddSubItems(subItems, FunctionGlobalItemType.ComboItem);
            if (selectFirst) item.SelectFirstItem();
            return item;
        }
		/// <summary>
		/// Vrátí položku, která bude zobrazena jako ComboItem
		/// </summary>
		/// <param name="creator">Ten, kdo tuto položku vytváří. Předává se this.</param>
		/// <param name="functionID">Identifikátor funkce. Enum je nejvhodnější. V konkrétní aplikaci se přetypuje zpátky. Tato hodnota přichází do cílových metod typicky v property RequestData.</param>
		/// <param name="image">Obrázek, může být null</param>
		/// <param name="text">Text do položky</param>
		/// <param name="toolTipText">Nápověda</param>
		/// <returns></returns>
		public static FunctionGlobalItem CreateComboItem(object creator, IComparable functionID, Image image, string text, string toolTipText)
		{
			FunctionGlobalItem item = new FunctionGlobalItem(creator.GetType(), functionID, FunctionGlobalItemType.ComboItem, text, toolTipText, image);
			return item;
		}
		/// <summary>
		/// Přidí do svého seznamu SubItems položky z daného seznamu, pokud odpovídají některému z daných typů.
		/// </summary>
		/// <param name="subItems">Seznam položek</param>
		/// <param name="itemTypeList">Seznam povolených typů</param>
		public void AddSubItems(IEnumerable<FunctionGlobalItem> subItems, params FunctionGlobalItemType[] itemTypeList)
		{
			if (subItems == null) return;
			List<FunctionGlobalItemType> et = new List<FunctionGlobalItemType>(itemTypeList);
			foreach (FunctionGlobalItem subItem in subItems)
			{
				if (et.Contains(subItem.ItemType))
					this._AddSubItem(subItem);
			}
		}
        /// <summary>
        /// Zajistí, že po zobrazení tohoto prvku (typicky ComboBox) bude vybrána první položka ze SubItems.
        /// </summary>
        public void SelectFirstItem()
        {
            this.SelectItemID = (this.SubItems.Count > 0 ? this.SubItems[0].FunctionID : null);
        }
        /// <summary>
		/// Přidá jednu položku do SubItem.
		/// </summary>
		/// <param name="subItem"></param>
		private void _AddSubItem(FunctionGlobalItem subItem)
		{
			this.SubItems.Add(subItem);
		}
		#endregion
	}
	/// <summary>
	/// Možné vizuální reprezentace funkce
	/// </summary>
	public enum FunctionGlobalItemType
	{
		/// <summary>
		/// Neviditelná funkce.
		/// </summary>
		None = 1,
		/// <summary>
		/// Oddělovač skupin
		/// </summary>
		Separator,
		/// <summary>
		/// Tlačítko. Pokud bude zadána property Text, pak se zobrazí za Image.
		/// Nemá mít SubItems. Může být umístěn jako SubItems v nadřazeném prvku.
		/// </summary>
		Button,
		/// <summary>
		/// Padací (rozbalovací) tlačítko.
		/// Musí mít SubItems.
		/// </summary>
		DropDownButton,
		/// <summary>
		/// Rozbalovací nabídka textových položek. 
		/// Musí mít vyplněný seznam SubItems, a teprve ty pak volají akci Run() do svého garanta funkce.
		/// </summary>
		ComboBox,
		/// <summary>
		/// Položka rozbalovací nabídky. Nemůže mít svoje další SubItems.
		/// </summary>
		ComboItem
	}
	#endregion
}
